package com.xu.tools.springtools;

import com.xu.tools.enums.CommonErrorCodeEnum;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.style.StylerUtils;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Spring常用工具类Demo
 *
 * @author xuguan
 * @since 2023/8/22
 */
@SuppressWarnings({"ResultOfMethodCallIgnored", "SpellCheckingInspection"})
public class SpringUtilsDemo {

    /**
     * ObjectUtils {@link  org.springframework.util.ObjectUtils} 使用示例
     */
    public static void useObjectUtils() {
        // 对象判空
        ObjectUtils.isEmpty(new Object()); // false
        // 比较两个对象是否相等, 数组比较每个元素是否相等
        ObjectUtils.nullSafeEquals(new int[]{1, 2, 3}, new int[]{1, 2, 4}); // false
        // 取出Optional中的值, Optional为空返回null
        ObjectUtils.unwrapOptional(Optional.ofNullable("hello")); // hello
        // 字符串形式输出对象, null->"", 数组->{item1, item2, item3, ...}, 对象->地址值
        ObjectUtils.getDisplayString(new Object()); // java.lang.Object@56ac3a89
        // 字符串形式输出对象, null->"null", 数组->{item1, item2, item3, ...}, 对象->地址值
        ObjectUtils.nullSafeToString(new Object()); // java.lang.Object@72d818d1
        // 返回对象整体标识
        ObjectUtils.identityToString(""); // java.lang.String@59494225
        // 返回对象16进制地址
        ObjectUtils.getIdentityHexString(""); // 72d818d1
        // 计算对象的哈希值
        ObjectUtils.nullSafeHashCode(new Object()); // 1926764753
        // 判断对象是否是数组
        ObjectUtils.isArray(new byte[]{}); // true
        // 判断对象是否是可检异常
        ObjectUtils.isCheckedException(new IOException()); // true
        // 判断捕获的异常是否是指定异常的实例
        ObjectUtils.isCompatibleWithThrowsClause(new NullPointerException(), RuntimeException.class); // true
        // 判断数组中是否包含元素
        ObjectUtils.containsElement(new String[]{"1", "2", "3"}, "1"); // true
        // 判断枚举中是否包含常量值字符串, 默认忽略大小写
        ObjectUtils.containsConstant(CommonErrorCodeEnum.values(), "ok"); // true
        // 判断枚举中是否包含常量值字符串, caseSensitive:是否大小写敏感, 不传默认false
        ObjectUtils.containsConstant(CommonErrorCodeEnum.values(), "ok", false); // true
        // 通过枚举名称获取枚举对象
        ObjectUtils.caseInsensitiveValueOf(CommonErrorCodeEnum.values(), "ok"); // OK(200, "OK")
        // 将数组转换为对象数组
        ObjectUtils.toObjectArray(new String[]{"1", "2", "3"}); // ["1","2","3"]
        // 在数组末尾追加元素
        ObjectUtils.addObjectToArray(new String[]{"1", "2", "3"}, "4"); // [1, 2, 3, 4]
    }

    /**
     * StringUtils {@link  org.springframework.util.StringUtils} 使用示例
     */
    public static void useStringUtils() {
        String s = "hello world";
        /* --------------------------------------- 字符串判断工具 --------------------------------------- */
        // 判断字符串是否为null或者"", "  "这种包含空白符的字符串为非空
        StringUtils.isEmpty(s);
        // 判断字符串是否不为null且长度大于0, "  "这种包含空白符的字符串长度大于0
        StringUtils.hasLength(s);
        // 判断字符串是否包含实际内容, 即不为null且长度大于0且非仅包含空白符
        StringUtils.hasText(s);
        // 判断字符串是否包含空白符
        StringUtils.containsWhitespace(s);
        // 判断字符串是否以指定字符串开始, 忽略大小写
        StringUtils.startsWithIgnoreCase(s, "HELLO");
        // 判断字符串是否以指定字符串结束, 忽略大小写
        StringUtils.endsWithIgnoreCase(s, "WORLD");
        // 判断字符串从指定索引位置开始到字符串末尾, 是否与指定字符串相匹配
        StringUtils.substringMatch(s, 6, "world");

        /* --------------------------------------- 字符串操作工具 --------------------------------------- */
        // 去除字符串头部空白字符
        StringUtils.trimLeadingWhitespace(s);
        // 去除字符串尾部空白字符
        StringUtils.trimTrailingWhitespace(s);
        // 去除字符串所有空白字符
        StringUtils.trimAllWhitespace(s);
        // 去除字符串第一个字符, 只有指定字符与第一个字符相匹配才会去除
        StringUtils.trimLeadingCharacter(s, 'h');
        // 去除字符串最后一个字符, 只有指定字符与最后一个字符相匹配才会去除
        StringUtils.trimTrailingCharacter(s, 'd');
        // 指定字符串替换, oldPattern:替换前的字符串, newPattern:替换后的字符串
        StringUtils.replace(s, "hello", "hi"); // hi, world
        // 删除字符串所有的指定子字符串
        StringUtils.delete(s, "hello "); // world
        // 删除字符串所有指定字符串所包含的字符
        StringUtils.deleteAny(s, "hlo"); // e wrd
        // 统计字符串中指定子串的个数
        StringUtils.countOccurrencesOf(s, "l");
        // 将字符串首字母大写
        StringUtils.capitalize("hello"); // Hello
        // 将字符串首字母小写
        StringUtils.uncapitalize("Hello"); // hello
        // 完全限定名以.分隔, 返回最后一个元素
        StringUtils.unqualify("com.xu.tools.springtools.SpringUtilsDemo"); // SpringUtilsDemo
        // 完全限定名指定分隔符, 返回最后一个元素
        StringUtils.unqualify("com/xu/tools/springtools/SpringUtilsDemo", '/'); // SpringUtilsDemo
        // 将字符串用单引号引起来
        StringUtils.quote(s); // 'hello world'
        // 如果对象为String类型将字符串用单引号引起来, 否则返回原对象
        StringUtils.quoteIfString(1); // 1

        String splitStr = "h| e |l|l|o||";
        // 将字符串按照指定分隔符(如果有多个则按第一个分隔符分隔)一分为二
        StringUtils.split(splitStr, "|"); // [h,  e |l|l|o||]
        // 将分隔列表的字符串转换为字符串数组
        StringUtils.delimitedListToStringArray(splitStr, "|"); // [h, e ,l,l,o, , ]
        // 将分隔列表的字符串转换为字符串数组, 并将指定字符串置空
        StringUtils.delimitedListToStringArray(splitStr, "|", "l"); // [h, e ,,,o, , ]
        // 将分隔列表的字符串转换为字符串数组, 对每个子元素首尾去空, 忽略为空的元素
        StringUtils.tokenizeToStringArray(splitStr, "|"); // [h,e,l,l,o]
        // 将分隔列表的字符串转换为字符串数组, trimTokens:是否对每个子元素首尾去空, ignoreEmptyTokens:是否忽略为空的元素
        StringUtils.tokenizeToStringArray(splitStr, "|", true, true); // [h,e,l,l,o]

        String commaStr = "h,e,l,l,o";
        // 将英文逗号分隔的字符串转换为Set集合(去重)
        StringUtils.commaDelimitedListToSet(commaStr);
        // 将英文逗号分隔的字符串转换为字符串数组
        StringUtils.commaDelimitedListToStringArray(commaStr);

        List<String> strList = Arrays.asList("h", "e", "l", "l", "o");
        // 将集合转换为英文逗号分隔的字符串
        StringUtils.collectionToCommaDelimitedString(strList);
        // 将集合转换为指定分隔符分隔的字符串
        StringUtils.collectionToDelimitedString(strList, "|");
        // 将集合转换为指定分隔符分隔的字符串, prefix:添加前缀, suffix:添加后缀
        StringUtils.collectionToDelimitedString(strList, "|", "pre-", "-suf"); // pre-h-suf|pre-e-suf|pre-l-suf|pre-l-suf|pre-o-suf
        // 将集合转换为字符串数组
        StringUtils.toStringArray(strList);

        String[] strArray = {"h", "e", "l", "l", "o"};
        // 将对象数组转换为英文逗号分隔的字符串
        StringUtils.arrayToCommaDelimitedString(strArray); // h,e,l,l,o
        // 将对象数组转换为指定分隔符分隔的字符串
        StringUtils.arrayToDelimitedString(strArray, "|"); // h|e|l|l|o
        // 对字符串数组去重
        StringUtils.removeDuplicateStrings(strArray); // [h,e,l,o]
        // 对字符串数组中每个元素进行头尾去空
        StringUtils.trimArrayElements(strArray); // [h,e,l,l,o]
        // 对字符串数组进行追加元素
        StringUtils.addStringToArray(strArray, "add");
        // 对字符串数组进行顺序排序, 会修改原数组
        StringUtils.sortStringArray(strArray); // [e, h, l, l, o]

        String[] propStrArray = {"k1:v1", "k2:v2"};
        // 将字符串数组按照指定分隔符分隔为key-value键值对, 最后转换为Properties对象
        StringUtils.splitArrayElementsIntoProperties(propStrArray, ":");
        // 先将字符串数组每个元素删除指定任意字符, 然后按照指定分隔符分隔为key-value键值对, 最后转换为Properties对象
        StringUtils.splitArrayElementsIntoProperties(propStrArray, ":", "kv");
        // 连接两个字符串数组
        StringUtils.concatenateStringArrays(strArray, propStrArray);

        /* --------------------------------------- 路径相关工具 --------------------------------------- */
        // uri解码
        StringUtils.uriDecode(UriComponentsBuilder.fromUriString("https://xuguan.eu.org/tags").path("/工具").toUriString(), StandardCharsets.UTF_8);
        // 将windows路径的\\转为/, 去除路径中间的..路径
        StringUtils.cleanPath("src/../src/main/java/../java/com/xu/tools/springtools/../springtools/SpringUtilsDemo.java"); // src/main/java/com/xu/tools/springtools/SpringUtilsDemo.java
        // 比较两个路径cleanPath后是否相同
        StringUtils.pathEquals("src/../src/main/java/../java/com/xu/tools/springtools/../springtools/SpringUtilsDemo.java", "src/../src/main/../main/java/com/xu/tools/springtools/SpringUtilsDemo.java"); // true
        // 获取相对路径的完整路径
        StringUtils.applyRelativePath(StringUtils.cleanPath("D:\\JetBrains\\project\\java-tools"), "src/main/java/com/xu/tools/springtools/SpringUtilsDemo.java"); // D:/JetBrains/project/src/main/java/com/xu/tools/springtools/SpringUtilsDemo.java
        // 获取文件名(带扩展名)
        StringUtils.getFilename("src/main/java/com/xu/tools/springtools/SpringUtilsDemo.java"); // SpringUtilsDemo.java
        // 获取文件扩展名
        StringUtils.getFilenameExtension("src/main/java/com/xu/tools/springtools/SpringUtilsDemo.java"); // java
        // 获取不带文件扩展名的文件路径
        StringUtils.stripFilenameExtension("src/main/java/com/xu/tools/springtools/SpringUtilsDemo.java"); // src/main/java/com/xu/tools/springtools/SpringUtilsDemo

        /* --------------------------------------- 时间相关工具 --------------------------------------- */
        // 将字符串解析为Locale区域对象
        StringUtils.parseLocale("zh_CN"); // zh_CN
        // 将字符串解析为Locale区域对象
        StringUtils.parseLocaleString("zh_CN"); // zh_CN
        // 将字符串解析为TimeZone时区对象
        StringUtils.parseTimeZoneString("Asia/Shanghai");
    }

    /**
     * CollectionUtils {@link  org.springframework.util.CollectionUtils} 使用示例
     */
    public static void useCollectionUtils() {
        // 判断Collection或者Map集合是否为空
        CollectionUtils.isEmpty(new ArrayList<>());
        // 判断迭代器是否包含指定元素
        CollectionUtils.contains(Arrays.asList("1", "2", "3").iterator(), "1");
        // 判断集合是否包含指定元素
        CollectionUtils.containsInstance(Arrays.asList("1", "2", "3"), "1");
        // 判断两个集合是否包含重复元素
        CollectionUtils.containsAny(Arrays.asList("1", "2", "3"), Arrays.asList("3", "4", "5"));
        // 判断集合是否只包含唯一对象(可以多个)
        CollectionUtils.hasUniqueObject(Arrays.asList("1", "1", "1")); // true
        // 返回集合中元素的Class字节码对象
        CollectionUtils.findCommonElementType(Arrays.asList("1", "2", "3")); // Class<String>
        // 返回预选集合中包含的第一个元素
        CollectionUtils.findFirstMatch(Arrays.asList("1", "2", "3"), Arrays.asList("1", "2", "3")); // 1
        // 按照类型查找集合中的元素, 有0个或多个满足条件返回null
        CollectionUtils.findValueOfType(Arrays.asList("1", 2), Integer.class); // 2
        // 按照类型顺序查找集合中的元素, 不为null直接返回
        CollectionUtils.findValueOfType(Arrays.asList("1", 2), new Class[]{String.class, Integer.class}); // 1
        // 返回集合最后一个元素
        CollectionUtils.lastElement(Arrays.asList("1", "2", "3"));
        // 将数组转换为List集合
        CollectionUtils.arrayToList(new String[]{"1", "2", "3"});
        // 获取List或Set集合最后一个元素
        CollectionUtils.lastElement(Arrays.asList("1", "2", "3"));
        // 将Map转换为MultiValueMap
        CollectionUtils.toMultiValueMap(new HashMap<String, List<String>>() {{
            put("k1", Arrays.asList("v11", "v12", "v13"));
            put("k2", Arrays.asList("v21", "v22", "v23"));
        }});
    }

    /**
     * Web常用方法使用示例<br>
     * {@link org.springframework.web.util.UriComponentsBuilder}
     * {@link org.springframework.web.util.UriUtils}
     * {@link org.springframework.util.StringUtils}
     * {@link org.springframework.web.context.request.ServletRequestAttributes}
     * {@link org.springframework.util.AntPathMatcher}
     */
    public static void useWebUtils() {
        // url编码
        String encodeUrl = UriComponentsBuilder.newInstance().scheme("https").host("xuguan.eu.org").path("/categories/网络").toUriString();
        // url解码
        StringUtils.uriDecode(encodeUrl, StandardCharsets.UTF_8);
        UriUtils.decode(encodeUrl, StandardCharsets.UTF_8);

        // springboot获取servlet
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (!ObjectUtils.isEmpty(servletRequestAttributes)) {
            HttpServletRequest request = servletRequestAttributes.getRequest();
            HttpServletResponse response = servletRequestAttributes.getResponse();
        }

        // url路径匹配
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        antPathMatcher.match("/web/**/list", "/web/a/b/list"); // true
    }


    /**
     * File常用方法使用示例<br>
     * {@link org.springframework.util.FileCopyUtils}
     * {@link org.springframework.util.StreamUtils}
     * {@link org.springframework.util.FileSystemUtils}
     * {@link org.springframework.util.ResourceUtils}
     */
    public static void useFileUtils() {
        try {
            // // FileCopyUtils主要处理字符流
            // FileCopyUtils.copy("hello world", new FileWriter("./file/file.txt"));
            // String copyToString = FileCopyUtils.copyToString(new FileReader("./file/file.txt"));
            // byte[] bytes = FileCopyUtils.copyToByteArray(new ByteArrayInputStream(new byte[]{1, 2, 4}));
            //
            // // FileCopyUtils主要处理字节流
            // StreamUtils.copy(new FileInputStream("./file/file.txt"), new FileOutputStream("./file/file2.txt"));
            // String copyToString1 = StreamUtils.copyToString(new FileInputStream("./file/file.txt"), StandardCharsets.UTF_8);
            // byte[] bytes1 = StreamUtils.copyToByteArray(new FileInputStream("./file/file.txt"));
            //
            // // 递归删除目录下所有文件
            // FileSystemUtils.deleteRecursively(new File("./file"));
            // // 递归拷贝目录下所有文件
            // FileSystemUtils.copyRecursively(new File("./file"), new File("./file2"));

            // 获取resources目录下资源文件
            File file = ResourceUtils.getFile("classpath:application.yml");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 类型解析常用方法使用示例<br>
     * {@link org.springframework.core.ResolvableType}
     */
    public static void useTypeUtils() throws IOException {
        final ParameterizedTypeReference<HashMap<String, Integer>> ptr = new ParameterizedTypeReference<>() {
        };
        final ResolvableType rt = ResolvableType.forInstance(ptr).as(ParameterizedTypeReference.class);
        final ResolvableType[] generics = rt.getGenerics();

        final ResolvableType rt1 = ResolvableType.forType(ptr.getType());
        final ResolvableType[] generics1 = rt1.getGenerics();

        Assert.isTrue(generics[0].getGenerics()[0].resolve() == String.class, "校验第一个泛型是String类型失败");
        Assert.isTrue(generics[0].getGenerics()[1].resolve() == Integer.class, "校验第二个泛型是Integer类型失败");

        Assert.isTrue(generics1[0].resolve() == String.class, "校验第一个泛型是String类型失败");
        Assert.isTrue(generics1[1].resolve() == Integer.class, "校验第二个泛型是Integer类型失败");
    }

    /**
     * spel表达式解析工具类
     * {@link org.springframework.expression.spel.support.StandardEvaluationContext}
     * {@link org.springframework.expression.spel.standard.SpelExpressionParser}
     */
    public static void spelUtils() {
        final String id = UUID.randomUUID().toString();

        final StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable("id", id);

        final SpelExpressionParser parser = new SpelExpressionParser();
        final String value = parser.parseExpression("#id").getValue(context, String.class);

        Assert.isTrue(Objects.equals(id, value), "校验id和value相等失败");
    }

    /**
     * 占位符填充工具类
     * {@link org.springframework.util.PropertyPlaceholderHelper}
     */
    public static void propertyPlaceholderUtils() {
        PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");
        Properties prop = new Properties();
        prop.setProperty("name", "张三");
        prop.setProperty("age", "18");
        final String result = helper.replacePlaceholders("这是一个示例, 我叫${name}, 今年${age}岁", prop);
        Assert.isTrue(Objects.equals("这是一个示例, 我叫张三, 今年18岁", result), "校验替换后的值匹配文本失败");
    }

    /**
     * 对象输出样式美化工具类
     * {@link org.springframework.core.style.StylerUtils}
     */
    public static void stylerUtils() {
        // list['1', '2', '3']
        StylerUtils.style(Arrays.asList("1", "2", "3"));
        // map[1 -> 'a', 2 -> 'b', 3 -> 'c']
        StylerUtils.style(new HashMap<Integer, String>() {
            {
                put(1, "a");
                put(2, "b");
                put(3, "c");
            }
        });

        final StopWatch sw = new StopWatch(Thread.currentThread().getName());
        sw.start();
        BeanUtils.instantiateClass(ArrayList.class);
        sw.stop();
        System.out.println("cost=" + sw.getTotalTimeMillis());
    }

    /**
     * 对象输出样式美化工具类
     * {@link org.springframework.util.DigestUtils}
     * {@link org.springframework.util.Base64Utils}
     */
    public static void codecUtil() {
        // 32位md5加密
        DigestUtils.md5DigestAsHex("张三".getBytes(StandardCharsets.UTF_8));

        // base64
        final byte[] encode = Base64Utils.encode("张三".getBytes(StandardCharsets.UTF_8));
        Base64Utils.decode(encode);
        final String encodeToString = Base64Utils.encodeToString("张三".getBytes(StandardCharsets.UTF_8));
        Base64Utils.decodeFromString(encodeToString);
    }

    public static void main(String[] args) throws Exception {
        // useObjectUtils();
        // useStringUtils();
        // useCollectionUtils();
        // useWebUtils();
        // useFileUtils();
        // useTypeUtils();
        // spelUtils();
        // propertyPlaceholderUtils();
        stylerUtils();
        // codecUtil();
    }
}